package com.cx.base.day16_映射.composed_annotation;

import org.springframework.core.annotation.AliasFor;
import org.springframework.core.annotation.AnnotatedElementUtils;

import java.lang.annotation.Annotation;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Arrays;
import java.util.Objects;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

/**
 * @Author songyahui
 * @Date 2023/9/1 17:50
 * @Description 组合注解：将已有注解合并到新注解上获得的一种复合注解，可以避免造轮子且实现功能的强化；
 * 合并注解：并到新注解上的已有注解合
 */
public class SynthesizedAnnotationTest {

    @Target({ANNOTATION_TYPE, FIELD, TYPE})
    @Retention(RUNTIME)
    @interface Test1 {
        String test1() default "test1";
    }

    @Target({ANNOTATION_TYPE, FIELD, TYPE})
    @Retention(RUNTIME)
    @Test1
    @interface Test2 {
        @AliasFor(annotation = Test1.class, attribute = "test1")
        String test2() default "test2";
    }

    @Target({ANNOTATION_TYPE, FIELD, TYPE})
    @Retention(RUNTIME)
    @Test2
    @interface Test3 {
        /**
         * AliasFor注解用来表示要覆盖Test2注解中的test2()属性方法，
         * annotation属性声明的注解类必须存在于该注解的元注解上
         * attribute属性声明的值必须存在于Test2注解属性方法中(即Test2注解的test2方法)
         */
        @AliasFor(annotation = Test2.class, attribute = "test2")
        String test3() default "test3";
    }

    /**
     * 只有@Test3注解，但是Test3注解上组合了@Test2注解，并将该注解的test3方法值用来覆盖Test2注解中的test2方法
     * 即更低层次声明的覆盖规则，会覆盖更高层次的属性方法值，即调用高层次的注解方法值实际显示的是低层所赋的值
     * 当然也可以将组合注解作用于更高层次，如Test3组合Test2,Test2组合Test1，然后将Test3作用于元素，通过工具类获取Test1注解覆盖的属性值
     */
    @Test3(test3 = "覆盖Test2中的test2方法")
    static class Element {
    }

    /**
     * 当@Test2直接做注解使用时会导致@AliasFor失效;
     * 如下：@Test2注解的test2属性最终打印的是'直接合并到Element',而不是'覆盖Test2中的test2方法'
     */
    @Test2(test2 = "直接合并到Element")
    @Test3(test3 = "覆盖Test2中的test2方法")
    static class Element2 {
    }

    public static void main(String[] args) {
        // 1、解析组合注解
        Annotation[] annotations = Element.class.getAnnotations();
        for (Annotation annotation : annotations) {
            System.out.println("-----获取注解------");
            System.out.println(annotation);
            // 获取注解上的注解需要间接操作，如下
            System.out.println("-----获取注解上的注解（又叫合并注解，mergeAnno）------");
            Arrays.stream(annotation.annotationType().getAnnotations())
                    .forEach(System.out::println);
        }
        System.out.println("-----1/获取注解上的注解（又叫合并注解，mergeAnno）—— 利用工具方法优化 ------");
        // 优化：Spring 在 spring-core 中的 org.springframework.core.annotation 包提供了大量关于注解的工具类。
        // 使用其中的 AnnotatedElementUtils 工具类就可以方便地获取组合注解中的每一个注解。
        Test1 test1 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test1.class);
        System.out.println(test1.test1());

        //2、AliasFor演示：可以把注解的属性值覆盖到合并注解的属性值上
        System.out.println("-----2/获取AliasFor指定覆盖的注解的属性 ------");
        // 虽然调用了Test2注解的test2方法，但是实际显示的是Test3注解中的test3属性声明的值
        Test2 test2 = AnnotatedElementUtils.getMergedAnnotation(Element.class, Test2.class);
        // 如果out信息为'覆盖Test2属性中的test2方法' 则说明Test2的test2属性被覆盖了
        System.out.println(test2.test2());
        System.out.println(test1.test1());

        //3、AliasFor参数覆盖失效的情况
        System.out.println("-----3/另一种情况: AliasFor参数覆盖失效 ------");
        Test3 test3 = AnnotatedElementUtils.getMergedAnnotation(Element2.class, Test3.class);
        System.out.println(test3.test3());
        test2 = AnnotatedElementUtils.getMergedAnnotation(Element2.class, Test2.class);
        test1 = AnnotatedElementUtils.getMergedAnnotation(Element2.class, Test1.class);
        // 如果out信息为'覆盖Test2属性中的test2方法' 则说明Test2的test2属性被覆盖了
        System.out.println(test2.test2());
        System.out.println(test1.test1());

        //总结：
        // 组合注解可以将原有注解合并过来实现功能强化
        // AliasFor可以将注解属性获取的数据传递到合并注解的属性上
        // 合并注解直接使用会导致AliasFor覆盖失效，如上Element2添加@Test2

    }
}
